package com.itrui.buyitbackend.controller;

import com.google.gson.Gson;
import com.itrui.buyitbackend.pojo.AllMessage;
import com.itrui.buyitbackend.pojo.MessageReceive;
import com.itrui.buyitbackend.pojo.MessageSend;
import com.itrui.buyitbackend.common.MessageType;
import com.itrui.buyitbackend.service.MessageReceiveService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Logger;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * @ServerEndpoint 注解是一个类层次的注解，它的功  能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint("/ws/{userId}")
@Controller
@Slf4j
public class WebSocketController {

    private static MessageReceiveService messageReceiveService;


    // 静态方法、在SpringBoot启动时被调用
    public static void setMyService(MessageReceiveService messageReceiveService) {
        WebSocketController.messageReceiveService = messageReceiveService;
    }


    private static final Logger logger = Logger.getLogger(WebSocketController.class.getName());
    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //用来存放每个客户端对应的MyWebSocket对象。
    //使用Map来存放，其中Key可以为用户标识
    private static HashMap<Integer, WebSocketController> webSocketMap =new HashMap<>();
    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    private int userId;

//    private static Map<Integer, List<String>> history = new HashMap<>();
    private static Map<Integer, List<Integer>> blackLists = new HashMap<>();

    static {
        blackLists.put(3, new ArrayList<>(Arrays.asList(1,4)));
    }

    private static Set<Integer> rejectShareUsers = new HashSet<>();
    static {
        rejectShareUsers.add(4);
    }


    /**
     * 连接建立成功调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(@PathParam("userId")int userId, Session session) throws IOException {

        log.info("进来了");
        this.session = session;
        this.userId = userId;
        webSocketMap.put(userId ,this); //加入map中

        addOnlineCount();           //在线数加1
        logger.warning("有新连接加入！当前在线人数为" + getOnlineCount());
       /* sendHistory(target);*/
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(){

        webSocketMap.remove(this);//从map中删除
        subOnlineCount();           //在线数减1
        logger.warning("有一连接关闭！当前在线人数为" + getOnlineCount());


    }

    /**
     * 收到客户端消息后调用的方法
     * @param msg_in_str 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String msg_in_str, Session session) throws IOException {

        logger.info("处理："+msg_in_str);
        Gson gson = new Gson();

        //处理进入的消息
        MessageReceive msg_in = gson.fromJson(msg_in_str, MessageReceive.class);

        int type = msg_in.getType();
        int target = msg_in.getTarget();
        String content = msg_in.getContent();
        msg_in.setSender(this.userId);

        //TODO 增加已读消息处理方法

        //添加黑名单
        if (type == MessageType.BLACK){
            {
                if (!blackLists.containsKey(this.userId)) {
                    blackLists.put(this.userId, new ArrayList<>());
                }
                blackLists.get(this.userId).add(target);
            }
            //TODO 添加到黑名单数据库表： 列1放userId  列2放target
            messageReceiveService.addBlackList(this.userId,target,type);
        }

        //检查拒收
        MessageSend msg_reject = checkReject(type, target);
        if (msg_reject != null){
            this.sendMessage(gson.toJson(msg_reject));
            return;
        }

        //封装输出的消息
        MessageSend msg_out = new MessageSend(type, this.userId, target, content);
        String msg_out_str = gson.toJson(msg_out);

        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        msg_in.setDatetime(dateFormat.format(date));

        AllMessage allMessage = new AllMessage();
        allMessage.setSender(msg_in.getSender());
        allMessage.setTarget(msg_in.getTarget());
        allMessage.setContent(msg_in.getContent());
        allMessage.setType(msg_in.getType());
        allMessage.setMessageSendTime(msg_in.getDatetime());
        //发送
        WebSocketController item=webSocketMap.get(target);
        //TODO messageReceiveService.saveAllMessage(msg_in);//存到历史消息数据库
        messageReceiveService.saveAllMessage(allMessage);//存到历史消息数据库

        if (item == null){
            // 目标不在线，将消息存入列表/数据库，等待下次目标用户连接再一次性发送
            //获取当前时间

            logger.warning("存储消息:"+msg_out_str);
            messageReceiveService.saveMessage(msg_in);//存到未读消息数据库

//            if (!history.containsKey(target)){
//                history.put(target, new ArrayList<>());
//            }
//            history.get(target).add(msg_out_str);

        } else {
            //目标在线
            item.sendMessage(msg_out_str);
        }

    }

    private MessageSend checkReject(int type, int target){
        //TODO 检查是否在目标的黑名单内: 列1=target 且 列2=this.userId 5

        /*if (blackLists.containsKey(target) && blackLists.get(target).contains(userId)) {
            return new MessageSend(MessageType.REJECT_ALL);
        }*/
        if (messageReceiveService.isExistenceBlackList(target,userId,MessageType.BLACK) != null){
            return new MessageSend(MessageType.REJECT_ALL);
        }

        //检查对方是否接收商品消息
        /*if (type==MessageType.SHARE && rejectShareUsers.contains(target)){
            return new MessageSend(MessageType.REJECT_SHARE);
        }*/
        if (messageReceiveService.isExistenceBlackList(userId,target,MessageType.BLACK_SHARE) != null){
            return new MessageSend(MessageType.REJECT_SHARE);
        }

        //未拒收
        return null;
    }

    // TODO 修改为一次性发送历史消息方法
    public void sendHistory(int target) throws IOException {
        logger.info("发送历史消息开始>>>>>>>>>>>>>>>>>");

        /*if (history.containsKey(this.userId)){
            //获取
            for (String msg_history: history.get(this.userId)){
                this.sendMessage(msg_history);
            }
            history.remove(this.userId);
        }*/


        for(AllMessage message: messageReceiveService.getAllMessage(this.userId,target)){
            Gson gson = new Gson();
            String s = gson.toJson(message);
            this.sendMessage(s);

        }
        messageReceiveService.delMessgeById(this.userId,target);
        /*if (messageReceiveService.isUserExist(this.userId) != null){

            for(MessageReceive message: messageReceiveService.getMessage(this.userId)){
                Gson gson = new Gson();
                String s = gson.toJson(message);
                this.sendMessage(s);

            }

        }*/
        logger.info("发送历史消息结束<<<<<<<<<<<<<<<<<");
    }

    /**
     * 发生错误时调用
     * @param session 会话
     * @param error 错误
     */
    @OnError
    public void onError(Session session, Throwable error){
        logger.warning("发生错误,userId:"+this.userId);
        error.printStackTrace();
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解，是根据自己需要添加的方法。
     * @param message 消息
     * @throws IOException IO异常
     */
    //给客户端传递消息
    public void sendMessage(String message) throws IOException{
        log.info("发送给"+this.userId+message);
        this.session.getBasicRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketController.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketController.onlineCount--;
    }
}